home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / chrome / lightning.jar / content / lightning / messenger-overlay-sidebar.js < prev    next >
Encoding:
JavaScript  |  2007-09-26  |  23.0 KB  |  691 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Lightning code.
  15.  *
  16.  * The Initial Developer of the Original Code is Oracle Corporation
  17.  * Portions created by the Initial Developer are Copyright (C) 2005
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Mike Shaver <shaver@mozilla.org>
  22.  *   Vladimir Vukicevic <vladimir@pobox.com>
  23.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  24.  *   Dan Mosedale <dmose@mozilla.org>
  25.  *   Joey Minta <jminta@gmail.com>
  26.  *   Simon Paquet <bugzilla@babylonsounds.com>
  27.  *   Stefan Sitter <ssitter@googlemail.com>
  28.  *   Thomas Benisch <thomas.benisch@sun.com>
  29.  *   Michael Buettner <michael.buettner@sun.com>
  30.  *   Philipp Kewisch <mozilla@kewis.ch>
  31.  *   Berend Cornelius <berend.cornelius@sun.com>
  32.  *
  33.  * Alternatively, the contents of this file may be used under the terms of
  34.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  35.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  36.  * in which case the provisions of the GPL or the LGPL are applicable instead
  37.  * of those above. If you wish to allow use of your version of this file only
  38.  * under the terms of either the GPL or the LGPL, and not to allow others to
  39.  * use your version of this file under the terms of the MPL, indicate your
  40.  * decision by deleting the provisions above and replace them with the notice
  41.  * and other provisions required by the GPL or the LGPL. If you do not delete
  42.  * the provisions above, a recipient may use your version of this file under
  43.  * the terms of any one of the MPL, the GPL or the LGPL.
  44.  *
  45.  * ***** END LICENSE BLOCK ***** */
  46.  
  47. var gLastShownCalendarView = null;
  48.  
  49. var CalendarController =
  50. {
  51.   defaultController: null,
  52.  
  53.   supportsCommand: function ccSC(command) {
  54.     switch (command) {
  55.       case "cmd_cut":
  56.       case "cmd_copy":
  57.       case "cmd_paste":
  58.       case "cmd_undo":
  59.       case "cmd_redo":
  60.       case "cmd_print":
  61.       case "cmd_printpreview":
  62.       case "button_print":
  63.       case "button_delete":
  64.       case "cmd_delete":
  65.         return true;
  66.     }
  67.     if (this.defaultController) {
  68.       return this.defaultController.supportsCommand(command);
  69.     }
  70.     return false;
  71.   },
  72.  
  73.   isCommandEnabled: function ccICE(command) {
  74.     switch (command) {
  75.       case "cmd_cut":
  76.       case "cmd_copy":
  77.         return currentView().getSelectedItems({}).length != 0;
  78.       case "cmd_paste":
  79.         return canPaste();
  80.       case "cmd_undo":
  81.         if (this.isCalendarInForeground()) {
  82.           goSetMenuValue(command, 'valueDefault');
  83.           if (canUndo()) {
  84.             return true;
  85.           }
  86.         }
  87.         break;
  88.       case "cmd_redo":
  89.         if (this.isCalendarInForeground()) {
  90.           goSetMenuValue(command, 'valueDefault');
  91.           if(canRedo()) {
  92.             return true;
  93.           }
  94.         }
  95.         break;
  96.       case "button_print":
  97.       case "cmd_print":
  98.         if (this.isCalendarInForeground()) {
  99.           return true;
  100.         }
  101.         break;
  102.       case "cmd_printpreview":
  103.         if (this.isCalendarInForeground()) {
  104.           return false;
  105.         }
  106.         break;
  107.       case "button_delete":
  108.       case "cmd_delete":
  109.         if (this.isCalendarInForeground()) {
  110.           var selectedItems = currentView().getSelectedItems({});
  111.           return selectedItems.length != 0;
  112.         }
  113.         break;
  114.     }
  115.     if (this.defaultController) {
  116.       return this.defaultController.isCommandEnabled(command);
  117.     }
  118.     return false;
  119.   },
  120.  
  121.   doCommand: function ccDC(command) {
  122.     // if the user invoked a key short cut then it is possible that we got
  123.     // here for a command which is really disabled. kick out if the
  124.     // command should be disabled.
  125.     if (!this.isCommandEnabled(command)) {
  126.       return;
  127.     }
  128.  
  129.     switch ( command )
  130.     {
  131.       case "cmd_cut":
  132.         cutToClipboard();
  133.         break;
  134.       case "cmd_copy":
  135.         copyToClipboard();
  136.         break;
  137.       case "cmd_paste":
  138.         pasteFromClipboard();
  139.         break;
  140.       case "cmd_undo":
  141.         if (this.isCalendarInForeground() && canUndo()) {
  142.           getTransactionMgr().undo();
  143.         }
  144.         break;
  145.       case "cmd_redo":
  146.         if (this.isCalendarInForeground() && canRedo()) {
  147.           getTransactionMgr().redo();
  148.         }
  149.         break;
  150.       case "button_print":
  151.       case "cmd_print":
  152.         if (this.isCalendarInForeground()) {
  153.           calPrint();
  154.           return;
  155.         }
  156.         break;
  157.       case "cmd_printpreview":
  158.         if (this.isCalendarInForeground()) {
  159.           return;
  160.         }
  161.         break;
  162.       case "button_delete":
  163.       case "cmd_delete":
  164.         if (this.isCalendarInForeground()) {
  165.           return;
  166.         }
  167.         break;
  168.     }
  169.     if (this.defaultController) {
  170.       this.defaultController.doCommand(command);
  171.     }
  172.   },
  173.  
  174.   onEvent: function ccOE(event) {
  175.     // do nothing here...
  176.   },
  177.   
  178.   isCalendarInForeground: function ccIC() {
  179.     return document.getElementById("displayDeck").selectedPanel.id == "calendar-view-box";
  180.   }
  181. };
  182.  
  183. function today()
  184. {
  185.     var d = Components.classes['@mozilla.org/calendar/datetime;1'].createInstance(Components.interfaces.calIDateTime);
  186.     d.jsDate = new Date();
  187.     return d.getInTimezone(calendarDefaultTimezone());
  188. }
  189.  
  190. function yesterday()
  191. {
  192.     var d = today();
  193.     d.day--;
  194.     return d;
  195. }
  196.  
  197. function nextMonth(dt)
  198. {
  199.     var d = new Date(dt);
  200.     d.setDate(1); // make sure we avoid "June 31" when we bump the month
  201.  
  202.     var mo = d.getMonth();
  203.     if (mo == 11) {
  204.         d.setMonth(0);
  205.         d.setYear(d.getYear() + 1);
  206.     } else {
  207.         d.setMonth(mo + 1);
  208.     }
  209.  
  210.     return d;
  211. }
  212.  
  213. var gMiniMonthLoading = false;
  214. function ltnMinimonthPick(minimonth)
  215. {
  216.     if (gMiniMonthLoading)
  217.         return;
  218.  
  219.     var jsDate = minimonth.value;
  220.     document.getElementById("ltnDateTextPicker").value = jsDate;
  221.     var cdt = jsDateToDateTime(jsDate);
  222.  
  223.     if (document.getElementById("displayDeck").selectedPanel != 
  224.         document.getElementById("calendar-view-box")) {
  225.         showCalendarView(gLastShownCalendarView);
  226.     }
  227.  
  228.     cdt = cdt.getInTimezone(currentView().timezone);
  229.     cdt.isDate = true;
  230.     currentView().goToDay(cdt);
  231. }
  232.  
  233. function ltnGoToDate()
  234. {
  235.     var goToDate = document.getElementById("ltnDateTextPicker");
  236.     if (goToDate.value) {
  237.         ltnMinimonthPick(goToDate);
  238.     }
  239. }
  240.  
  241. function ltnOnLoad(event)
  242. {
  243.     // Load the Calendar Manager
  244.     loadCalendarManager();
  245.  
  246.     // take the existing folderPaneBox (that's what thunderbird displays
  247.     // at the left side of the application window) and stuff that inside
  248.     // of the deck we're introducing with the contentPanel. this essentially
  249.     // rearranges the DOM tree and allows us to switch between content that
  250.     // lives inside of the left pane.
  251.     var folderPaneBox = document.getElementById("folderPaneBox");
  252.     var contentPanel = document.getElementById("contentPanel");
  253.     contentPanel.insertBefore(folderPaneBox, contentPanel.firstChild);
  254.  
  255.     // we're taking care of the mode toolbar (that's the small toolbar on
  256.     // the lower left with the 'mail', 'calendar', 'task' buttons on it).
  257.     // since we want to have this particular toolbar displayed at the
  258.     // top or bottom inside the folderPaneBox (basically on top or bottom
  259.     // of the window) we need to go to great length in order to get this
  260.     // damned thing working. i decided to dynamically place the toolbox
  261.     // inside the DOM tree, that appears to be the most clean solution to
  262.     // the problem. unfortunately, it didn't work out that easy. as soon
  263.     // as we call insertBefore() to place the node somewhere different, the
  264.     // constructor of the appropriate binding gets called again. this has
  265.     // the nasty side-effect that the toolbar get a bit confused, since it
  266.     // thinks it is customized, which just isn't the case. that's why we need
  267.     // to carry some internal toolbar properties over. and that's what those
  268.     // functions retrieveToolbarProperties() and restoreToolbarProperties()
  269.     // are all about.
  270.     var retrieveToolbarProperties = function(toolbox)
  271.     {
  272.       var toolbars = {};
  273.       var toolbar = toolbox.firstChild;
  274.       while (toolbar) {
  275.         if (toolbar.localName == "toolbar") {
  276.           if (toolbar.getAttribute("customizable") == "true") {
  277.             if (!toolbar.hasAttribute("customindex")) {
  278.               var propertybag = {};
  279.               propertybag.firstPermanentChild = toolbar.firstPermanentChild;
  280.               propertybag.lastPermanentChild = toolbar.lastPermanentChild;
  281.               toolbars[toolbar.id] = propertybag;
  282.             }
  283.           }
  284.         }
  285.         toolbar = toolbar.nextSibling;
  286.       }
  287.       return toolbars;
  288.     }
  289.     
  290.     var restoreToolbarProperties = function(toolbox,toolbars)
  291.     {
  292.       var toolbar = toolbox.firstChild;
  293.       while (toolbar) {
  294.         if (toolbar.localName == "toolbar") {
  295.           if (toolbar.getAttribute("customizable") == "true") {
  296.             if (!toolbar.hasAttribute("customindex")) {
  297.               var propertybag = toolbars[toolbar.id];
  298.               toolbar.firstPermanentChild = propertybag.firstPermanentChild;
  299.               toolbar.lastPermanentChild = propertybag.lastPermanentChild;
  300.             }
  301.           }
  302.         }
  303.         toolbar = toolbar.nextSibling;
  304.       }
  305.     }
  306.     
  307.     // DOMAttrModified handler that listens on the toolbox element
  308.     var onModified = function(aEvent)
  309.     {
  310.       if(aEvent.attrName == "location") {
  311.         var contentPanel = document.getElementById("contentPanel");
  312.         var modeToolbox = document.getElementById("mode-toolbox");
  313.         var palette = modeToolbox.palette;
  314.         modeToolbox.removeEventListener("DOMAttrModified", onModified, false);
  315.         var bag = retrieveToolbarProperties(modeToolbox);
  316.         if(aEvent.newValue == "top" && !aEvent.prevValue || aEvent.prevValue == "bottom") {
  317.           // place the mode toolbox at the top of the left pane
  318.           modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
  319.           modeToolbox.palette = palette;
  320.           var toolbar = document.getElementById("mode-toolbar");
  321.         } else if(aEvent.newValue == "bottom" && aEvent.prevValue == "top") {
  322.           // place the mode toolbox at the bottom of the left pane
  323.           modeToolbox = contentPanel.parentNode.appendChild(modeToolbox);
  324.           modeToolbox.palette = palette;
  325.         }
  326.         restoreToolbarProperties(modeToolbox,bag);
  327.         modeToolbox.addEventListener("DOMAttrModified", onModified, false);
  328.       }
  329.     }
  330.  
  331.     // install the handler that listens for modified 'location' attribute
  332.     // on the toolbox. the value is changed by the toolbar customize dialog.
  333.     var modeToolbox = document.getElementById("mode-toolbox");
  334.     if(modeToolbox.getAttribute("location") != "bottom") {
  335.       var palette = modeToolbox.palette;
  336.       var bag = retrieveToolbarProperties(modeToolbox);
  337.       modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
  338.       modeToolbox.palette = palette;
  339.       restoreToolbarProperties(modeToolbox,bag);
  340.     }
  341.     modeToolbox.addEventListener("DOMAttrModified", onModified, false);
  342.  
  343.     // To make sure the folder pane doesn't disappear without any possibility
  344.     // to get it back, we need to reveal it when the mode box is collapsed.
  345.     function modeBoxAttrModified(event) {
  346.         if (event.attrName == "collapsed") {
  347.             document.getElementById("folderPaneBox")
  348.                     .removeAttribute("collapsed");
  349.         }
  350.     }
  351.  
  352.     document.getElementById("ltnModeBox").addEventListener("DOMAttrModified",
  353.                                                            modeBoxAttrModified,
  354.                                                            true);
  355.  
  356.     // find last shown calendar view (persist="checked")
  357.     var availableViews = getViewDeck();
  358.     var numChilds = availableViews.childNodes.length;
  359.     for (var i = 0; i < numChilds; i++) {
  360.         var view = availableViews.childNodes[i];
  361.         var command = document.getElementById(view.id+"-command");
  362.         if (command && command.getAttribute("checked") == "true") {
  363.             gLastShownCalendarView = view.id.substring(0, view.id.indexOf('-'));
  364.             break;
  365.         }
  366.     }
  367.     if (!gLastShownCalendarView) {
  368.         gLastShownCalendarView = 'month';
  369.     }
  370.  
  371.     gMiniMonthLoading = true;
  372.  
  373.     var today = new Date();
  374.     var nextmo = nextMonth(today);
  375.  
  376.     document.getElementById("ltnMinimonth").value = today;
  377.     document.getElementById("ltnDateTextPicker").value = today;
  378.  
  379.     gMiniMonthLoading = false;
  380.  
  381.     // nuke the onload, or we get called every time there's
  382.     // any load that occurs
  383.     document.removeEventListener("load", ltnOnLoad, true);
  384.  
  385.     // Hide the calendar view so it doesn't push the status-bar offscreen
  386.     collapseElement(document.getElementById("calendar-view-box"));
  387.  
  388.     // fire up the alarm service
  389.     var alarmSvc = Components.classes["@mozilla.org/calendar/alarm-service;1"]
  390.                    .getService(Components.interfaces.calIAlarmService);
  391.     alarmSvc.timezone = calendarDefaultTimezone();
  392.     alarmSvc.startup();
  393.  
  394.     // Add an unload function to the window so we don't leak any listeners
  395.     document.getElementById("messengerWindow")
  396.             .addEventListener("unload", ltnFinish, false);
  397.  
  398.     document.getElementById("displayDeck")
  399.             .addEventListener("dayselect", observeViewDaySelect, false);
  400.  
  401.     prepareCalendarToDoUnifinder();
  402.  
  403.     // Make sure we update ourselves if the program stays open over midnight
  404.     scheduleMidnightUpdate(refreshUIBits);
  405.  
  406.     if (getPrefSafe("calendar.prototypes.wcap", false)) {
  407.         document.loadOverlay(
  408.             "chrome://lightning/content/sun-messenger-overlay-sidebar.xul",
  409.             null);
  410.     }
  411.  
  412.     // we need to put our new command controller *before* the one that
  413.     // gets installed by thunderbird. since we get called pretty early
  414.     // during startup we need to install the function below as a callback
  415.     // that periodically checks when the original thunderbird controller
  416.     // gets alive. please note that setTimeout with a value of 0 means that
  417.     // we leave the current thread in order to re-enter the message loop.
  418.     var injectCommandController = function inject() {
  419.       var controller = top.controllers.getControllerForCommand("cmd_undo");
  420.       if (!controller) {
  421.         setTimeout(injectCommandController, 0);
  422.       } else {
  423.         CalendarController.defaultController = controller;
  424.         top.controllers.insertControllerAt(0, CalendarController);
  425.       }
  426.     }
  427.     injectCommandController();
  428.  
  429.     getViewDeck().addEventListener("itemselect", onSelectionChanged, true);
  430. }
  431.  
  432. function onSelectionChanged(aEvent) {
  433.   var elements = document.getElementsByAttribute("disabledwhennoeventsselected", "true");
  434.   var selectedItems = aEvent.detail;
  435.   for (var i = 0; i < elements.length; i++) {
  436.     if (selectedItems.length >= 1) {
  437.       elements[i].removeAttribute("disabled");
  438.     } else {
  439.       elements[i].setAttribute("disabled", "true");
  440.     }
  441.   }
  442.   document.commandDispatcher.updateCommands('mail-toolbar');
  443.   document.commandDispatcher.updateCommands('calendar_commands');
  444. }
  445.  
  446. /* Called at midnight to tell us to redraw date-specific widgets.  Do NOT call
  447.  * this for normal refresh, since it also calls scheduleMidnightRefresh.
  448.  */
  449. function refreshUIBits() {
  450.     document.getElementById("ltnMinimonth").refreshDisplay();
  451.  
  452.     // refresh the current view, if it has ever been shown
  453.     var cView = currentView();
  454.     if (cView.initialized) {
  455.         cView.goToDay(cView.selectedDay);
  456.     }
  457.  
  458.     if (TodayPane.showsYesterday()) {
  459.       TodayPane.setDay(today());
  460.     }
  461.     // schedule our next update...
  462.     scheduleMidnightUpdate(refreshUIBits);
  463. }
  464.  
  465. function showCalendarView(type)
  466. {
  467.     gLastShownCalendarView = type;
  468.  
  469.     if (gCurrentMode != 'calendar') {
  470.         // This function in turn calls showCalendarView(), so return afterwards.
  471.         ltnSwitch2Calendar();
  472.         return;
  473.     }
  474.  
  475.     // If we got this call while a mail-view is being shown, we need to
  476.     // hide all of the mail stuff so we have room to display the calendar
  477.     var calendarViewBox = document.getElementById("calendar-view-box");
  478.     if (calendarViewBox.style.visibility == "collapse") {
  479.         collapseElement(GetMessagePane());
  480.         collapseElement(document.getElementById("threadpane-splitter"));
  481.         var searchBox = findMailSearchBox();
  482.         if (searchBox) {
  483.             collapseElement(searchBox);
  484.         }
  485.         uncollapseElement(calendarViewBox);
  486.     }
  487.  
  488.     var view = document.getElementById(type+"-view");
  489.     var rotated = document.getElementById("ltn-multiday-rotated");
  490.  
  491.     if (!view.initialized) {
  492.         // Set up this view with the current view-checkbox values
  493.         var workdaysMenu = document.getElementById("ltn-workdays-only");
  494.         view.workdaysOnly = (workdaysMenu.getAttribute("checked") == 'true');
  495.  
  496.         var tasksMenu = document.getElementById("ltn-tasks-in-view")
  497.         view.tasksInView = (tasksMenu.getAttribute("checked") == 'true');
  498.         view.showCompleted = !document.getElementById("hide-completed-checkbox").checked;
  499.  
  500.         view.rotated = (rotated.getAttribute("checked") == 'true');
  501.     }
  502.  
  503.     // Disable the menuitem when not in day or week view.
  504.     if (type == "day" || type == "week") {
  505.         rotated.removeAttribute("disabled");
  506.     } else {
  507.         rotated.setAttribute("disabled", true);
  508.     }
  509.  
  510.     document.getElementById("displayDeck").selectedPanel =  calendarViewBox;
  511.     switchToView(type);
  512.  
  513.     // Set the labels for the context-menu
  514.     var nextCommand = document.getElementById("context_next");
  515.     nextCommand.setAttribute("label", nextCommand.getAttribute("label-"+type));
  516.     var previousCommand = document.getElementById("context_previous")
  517.     previousCommand.setAttribute("label", previousCommand.getAttribute("label-"+type));
  518.  
  519.     // Set up the commands
  520.     var availableViews = getViewDeck();
  521.     for (var i = 0; i < availableViews.childNodes.length; i++) {
  522.         var view = availableViews.childNodes[i];
  523.         var command = document.getElementById(view.id+"-command");
  524.         if (view.id == type+"-view") {
  525.            command.setAttribute("checked", true);
  526.         } else {
  527.            command.removeAttribute("checked");
  528.         }
  529.     }
  530. }
  531.  
  532. function goToToday()
  533. {
  534.     // set the current date in the minimonth control;
  535.     // note, that the current view in the calendar-view-box is automatically updated
  536.     var currentDay = today();
  537.     document.getElementById("ltnMinimonth").value = currentDay.jsDate;
  538. }
  539.  
  540.  
  541. function toggleTodayPaneinMailMode()
  542. {
  543.   var oTodayPane = document.getElementById("today-pane-panel");
  544.   var todayPaneCommand = document.getElementById('cmd_toggleTodayPane');
  545.   if (oTodayPane.hasAttribute("collapsed")) {
  546.     oTodayPane.removeAttribute("collapsed");
  547.     oTodayPane.removeAttribute("collapsedinMailMode");
  548.     todayPaneCommand.setAttribute("checked","true");
  549.     document.getElementById("today-closer").setAttribute("checked", "false");
  550.   }
  551.   else {
  552.     oTodayPane.setAttribute("collapsed", true);
  553.     oTodayPane.setAttribute("collapsedinMailMode", "true");
  554.     todayPaneCommand.setAttribute("checked", "false");
  555.   }
  556. }
  557.  
  558. function selectedCalendarPane(event)
  559. {
  560.     var deck = document.getElementById("displayDeck");
  561.  
  562.     // If we're already showing a calendar view, don't do anything
  563.     if (deck.selectedPanel.id == "calendar-view-box")
  564.         return;
  565.  
  566.     deck.selectedPanel = document.getElementById("calendar-view-box");
  567.  
  568.     showCalendarView('week');
  569. }
  570.  
  571. function LtnObserveDisplayDeckChange(event)
  572. {
  573.     var deck = event.target;
  574.  
  575.     // Bug 309505: The 'select' event also fires when we change the selected
  576.     // panel of calendar-view-box.  Workaround with this check.
  577.     if (deck.id != "displayDeck") {
  578.         return;
  579.     }
  580.  
  581.     var id = null;
  582.     try { id = deck.selectedPanel.id } catch (e) { }
  583.  
  584.     // Now we're switching back to the mail view, so put everything back that
  585.     // we collapsed in showCalendarView()
  586.     if (id != "calendar-view-box") {
  587.         if (gCurrentMode != 'mail') {
  588.             ltnSwitch2Mail();
  589.         }
  590.         collapseElement(document.getElementById("calendar-view-box"));
  591.         uncollapseElement(GetMessagePane());
  592.         uncollapseElement(document.getElementById("threadpane-splitter"));
  593.         var searchBox = findMailSearchBox();
  594.         if (searchBox) {
  595.             uncollapseElement(searchBox);
  596.         }
  597.  
  598.         // Disable the rotate view menuitem
  599.         document.getElementById("ltn-multiday-rotated")
  600.                 .setAttribute("disabled", true);
  601.     }
  602. }
  603.  
  604. function ltnFinish() {
  605.     getCompositeCalendar().removeObserver(agendaTreeView.calendarObserver);
  606.  
  607.     finishCalendarToDoUnifinder();
  608.  
  609.     unloadCalendarManager();
  610. }
  611.  
  612. function ltnEditSelectedItem() {
  613.     var selectedItems = currentView().getSelectedItems({});
  614.     for each (var item in selectedItems) {
  615.         calendarViewController.modifyOccurrence(item);
  616.     }
  617. }
  618.  
  619. function ltnDeleteSelectedItem() {
  620.     var selectedItems = currentView().getSelectedItems({});
  621.     calendarViewController.deleteOccurrences(selectedItems.length,
  622.                                              selectedItems,
  623.                                              false,
  624.                                              false);
  625. }
  626.  
  627. // After 1.5 was released, the search box was moved into an optional toolbar
  628. // item, with a different ID.  This function keeps us compatible with both.
  629. function findMailSearchBox() {
  630.     var tb15Box = document.getElementById("searchBox");
  631.     if (tb15Box) {
  632.         return tb15Box;
  633.     }
  634.  
  635.     var tb2Box = document.getElementById("searchInput");
  636.     if (tb2Box) {
  637.         return tb2Box;
  638.     }
  639.  
  640.     // In later versions, it's possible that a user removed the search box from
  641.     // the toolbar.
  642.     return null;
  643. }
  644.  
  645. function updateOrientation() {
  646.  
  647.     var value = (document.getElementById("ltn-multiday-rotated")
  648.                          .getAttribute("checked") == 'true');
  649.  
  650.     var deck = getViewDeck();
  651.     for each (view in deck.childNodes) {
  652.         view.rotated = value;
  653.     }
  654. }
  655.  
  656. function toggleWorkdaysOnly() {
  657.     for each (view in getViewDeck().childNodes) {
  658.         view.workdaysOnly = !view.workdaysOnly;
  659.     }
  660.  
  661.     // Refresh the current view
  662.     currentView().goToDay(currentView().selectedDay);
  663. }
  664.  
  665. function toggleTasksInView() {
  666.     for each (view in getViewDeck().childNodes) {
  667.         view.tasksInView = !view.tasksInView;
  668.     }
  669.  
  670.     // Refresh the current view
  671.     currentView().goToDay(currentView().selectedDay);
  672. }
  673.  
  674. var gSelectFolder = SelectFolder;
  675. var gSelectMessage = SelectMessage;
  676.  
  677. SelectFolder = function(folderUri) {
  678.     document.getElementById("switch2mail").doCommand();
  679.     gSelectFolder(folderUri);
  680. }
  681.  
  682. SelectMessage = function(messageUri) {
  683.     document.getElementById("switch2mail").doCommand();
  684.     gSelectMessage(messageUri);
  685. }
  686.  
  687. document.getElementById("displayDeck").
  688.     addEventListener("select", LtnObserveDisplayDeckChange, true);
  689.  
  690. document.addEventListener("load", ltnOnLoad, true);
  691.